/* eslint-disable @typescript-eslint/no-explicit-any */
import { Subject } from 'await-notify';
import { CloudMQ, CloudTopic } from '../../cloud-ws';
import { DebugMessage } from '../../debug-mq';
import { global } from '../../global';
import { ICloudDebugVariable } from '../../interface';
import { uuid } from '../../utils';

/**
 * 调试服务，负责连接到 cloud 平台调试服务
 *
 * @author chitanda
 * @date 2022-12-01 15:12:13
 * @export
 * @class DebugService
 */
export class DebugService {
  private sessionId: string = '';

  /**
   * 是否已连接到远程调试服务器
   *
   * @author chitanda
   * @date 2022-12-01 15:12:58
   * @readonly
   * @private
   * @type {boolean}
   */
  private get isConnect(): boolean {
    return !!this.sessionId;
  }

  /**
   * 指令会话
   *
   * @author chitanda
   * @date 2022-12-01 15:12:36
   * @protected
   * @type {CloudTopic}
   */
  protected commandTopic?: CloudTopic;

  /**
   * 事件会话
   *
   * @author chitanda
   * @date 2022-12-01 15:12:42
   * @protected
   * @type {CloudTopic}
   */
  protected eventTopic?: CloudTopic;

  /**
   * cloud mqtt 连接实例
   *
   * @author chitanda
   * @date 2022-12-01 15:12:47
   * @protected
   * @type {CloudMQ}
   */
  protected mq: CloudMQ;

  /**
   * 调试连接结束
   *
   * @author chitanda
   * @date 2022-12-01 15:12:04
   * @protected
   */
  protected done = new Subject();

  protected _topic: string = '';

  set topic(id: string) {
    this._topic = id;
  }

  get topic(): string {
    return this._topic;
  }

  protected _system: string = '';

  set system(id: string) {
    this._system = id;
  }

  get system(): string {
    return this._system;
  }

  constructor() {
    this.mq = new CloudMQ(global.config.mqttAddress);
  }

  /**
   * 打开调试连接
   *
   * @author chitanda
   * @date 2022-12-01 15:12:49
   * @return {*}  {Promise<boolean>}
   */
  async openSession(): Promise<boolean> {
    const id = `vscode-debug:open-session:${uuid()}`;
    const result = await this.sendCommandBackData(id, '', 'OPENSESSION', false);
    this.done.notifyAll();
    if (result) {
      this.sessionId = result.sessionid;
      return true;
    }
    return false;
  }

  /**
   * 关闭调试连接
   *
   * @author chitanda
   * @date 2022-12-01 15:12:56
   * @return {*}  {Promise<boolean>}
   */
  async closeSession(): Promise<boolean> {
    const id = `vscode-debug:close-session:${this.sessionId}`;
    const result = await this.sendCommandBackData(
      id,
      '',
      'CLOSESESSION',
      false,
    );
    if (result) {
      this.sessionId = '';
      return true;
    }
    return false;
  }

  /**
   * 添加断点
   *
   * @author chitanda
   * @date 2022-12-01 15:12:51
   * @param {string} msg 指定需要添加断点的文件和行数，示例: net.ibizsys.AppExController:18
   * @return {*}  {Promise<boolean>}
   */
  addBreakpoint(msg: string): Promise<boolean> {
    const id = `vscode-debug:add-breakpoint:${this.sessionId}`;
    return this.sendCommand(id, msg, 'ADDBREAKPOINT');
  }

  /**
   * 删除断点
   *
   * @author chitanda
   * @date 2022-12-01 15:12:29
   * @param {string} msg 指定需要删除断点的文件和行数，示例: net.ibizsys.AppExController:18
   * @return {*}  {Promise<boolean>}
   */
  deleteBreakpoint(msg: string): Promise<boolean> {
    const id = `vscode-debug:delete-breakpoint:${this.sessionId}`;
    return this.sendCommand(id, msg, 'DELETEBREAKPOINT');
  }

  /**
   * 清楚所有的断点
   *
   * @author chitanda
   * @date 2022-12-01 15:12:44
   * @return {*}  {Promise<boolean>}
   */
  clearAllBreakpoint(): Promise<boolean> {
    const id = `vscode-debug:clear-all-breakpoint:${this.sessionId}`;
    return this.sendCommand(id, '', 'DELETEALLBREAKPOINTS');
  }

  /**
   * 单步跳过
   *
   * @author chitanda
   * @date 2022-12-01 15:12:58
   * @return {*}  {Promise<boolean>}
   */
  stepOver(): Promise<boolean> {
    const id = `vscode-debug:step-over:${this.sessionId}`;
    return this.sendCommand(id, '', 'STEPOVER');
  }

  /**
   * 单步调试
   *
   * @author chitanda
   * @date 2022-12-01 15:12:10
   * @return {*}  {Promise<boolean>}
   */
  stepInto(): Promise<boolean> {
    const id = `vscode-debug:step-into:${this.sessionId}`;
    return this.sendCommand(id, '', 'STEPINTO');
  }

  /**
   * 单步跳出
   *
   * @author chitanda
   * @date 2022-12-01 15:12:17
   * @return {*}  {Promise<boolean>}
   */
  stepOut(): Promise<boolean> {
    const id = `vscode-debug:step-out:${this.sessionId}`;
    return this.sendCommand(id, '', 'STEPOUT');
  }

  /**
   * 跳过执行
   *
   * @author chitanda
   * @date 2022-12-01 15:12:35
   * @return {*}  {Promise<boolean>}
   */
  run(): Promise<boolean> {
    const id = `vscode-debug:run:${this.sessionId}`;
    return this.sendCommand(id, '', 'RUN');
  }

  async eval(data: string): Promise<string | null> {
    const id = `vscode-debug:eval:${this.sessionId}`;
    const result = await this.sendCommandBackData(id, data, 'EVAL');
    if (result) {
      return result.data;
    }
    return null;
  }

  /**
   * 获取对象的子属性
   *
   * @author chitanda
   * @date 2022-12-01 22:12:55
   * @param {string} data
   * @return {*}  {(Promise<Record<string, ICloudDebugVariable> | null>)}
   */
  async listFields(
    data: string,
  ): Promise<Record<string, ICloudDebugVariable> | null> {
    console.log('listFields: ', data);
    const id = `vscode-debug:list-fields:${this.sessionId}`;
    const result = await this.sendCommandBackData(id, data, 'LISTFIELDS');
    if (result) {
      return result.data;
    }
    return null;
  }

  /**
   * 发送执行，值关心是否成功
   *
   * @author chitanda
   * @date 2022-12-01 15:12:14
   * @param {string} id
   * @param {(string | Record<string, unknown>)} message
   * @param {string} subtype
   * @return {*}  {Promise<boolean>}
   */
  async sendCommand(
    id: string,
    message: string | Record<string, unknown>,
    subtype: string,
  ): Promise<boolean> {
    const result = await this.sendCommandBackData(id, message, subtype);
    if (result) {
      return true;
    }
    return false;
  }

  /**
   * 发送指令，关心返回值
   *
   * @author chitanda
   * @date 2022-12-01 15:12:23
   * @param {string} id
   * @param {(string | Record<string, unknown>)} message
   * @param {string} subtype
   * @param {boolean} [waitDone=true]
   * @return {*}  {(Promise<DebugMessage | null>)}
   */
  sendCommandBackData(
    id: string,
    message: string | Record<string, unknown>,
    subtype: string,
    waitDone: boolean = true,
  ): Promise<DebugMessage | null> {
    // eslint-disable-next-line no-async-promise-executor
    return new Promise<DebugMessage | null>(async resolve => {
      if (this.isConnect === false && waitDone === true) {
        await this.done.wait(1000);
      }
      const topic = await this.getCommandTopic();
      const data =
        message instanceof Object
          ? JSON.stringify(message)
          : (message as string);
      const msg = JSON.stringify({
        id,
        type: 'COMMAND',
        subtype,
        data,
        systemid: this.system,
        sessionid: this.sessionId,
      });
      // 订阅事件回调，确认操作消息反馈。收到反馈结束回调
      {
        let timer: unknown;
        const e = await this.getEventTopic();
        const call = (str: string) => {
          const cloudMsg = new DebugMessage(str);
          if (cloudMsg.type === 'REPLY' && cloudMsg.id === id) {
            if (timer) {
              clearTimeout(timer as number);
              timer = null;
            }
            e.off(call);
            if (cloudMsg.code === 0) {
              console.log(`成功: ${id} - ${str}`);
              resolve(cloudMsg);
              return;
            }
            console.log(`失败: ${id} - ${str}`);
            resolve(null);
          }
        };
        e.on(call);
        // 超时机制，10秒无反馈认为超时
        timer = setTimeout(async () => {
          e.off(call);
          resolve(null);
          console.log(`等待反馈超时: ${id} - ${msg}`);
        }, 10000);
      }
      console.log('发送内容:', id, '-', msg);
      await topic.publish(msg);
    });
  }

  /**
   * 获取指令发送 topic
   *
   * @author chitanda
   * @date 2022-11-29 10:11:10
   * @protected
   * @return {*}  {Promise<CloudTopic>}
   */
  protected async getCommandTopic(): Promise<CloudTopic> {
    await this.mq.connect();
    if (!this.commandTopic) {
      this.commandTopic = await this.mq.onTopic(
        `debugger/command/${this.topic}`,
      );
    }
    return this.commandTopic;
  }

  /**
   * 获取接收事件 topic
   *
   * @author chitanda
   * @date 2022-11-29 10:11:32
   * @return {*}  {Promise<CloudTopic>}
   */
  async getEventTopic(): Promise<CloudTopic> {
    await this.mq.connect();
    if (!this.eventTopic) {
      this.eventTopic = await this.mq.onTopic(`debugger/event/${this.topic}`);
    }
    return this.eventTopic;
  }

  /**
   * 取消所有 topic 订阅
   *
   * @author chitanda
   * @date 2022-12-14 21:12:24
   * @return {*}  {Promise<boolean>}
   */
  async unTopic(): Promise<boolean> {
    if (this.commandTopic) {
      await this.mq.unTopic(`debugger/command/${this.topic}`);
      this.commandTopic = undefined;
    }
    if (this.eventTopic) {
      await this.mq.onTopic(`debugger/event/${this.topic}`);
      this.eventTopic = undefined;
    }
    return true;
  }
}
